package com.atguigu.gmall.search.service;


import com.alibaba.fastjson.JSON;
import com.atguigu.gmall.common.bean.ResponseVo;
import com.atguigu.gmall.pms.entity.*;
import com.atguigu.gmall.search.feign.GmallPmsClient;
import com.atguigu.gmall.search.feign.GmallWmsClient;
import com.atguigu.gmall.search.pojo.*;
import com.atguigu.gmall.wms.entity.WareSkuEntity;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.Index;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author mahaijun
 * @version 1.0.0
 * @ClassName SearchService.java
 * @Description TODO
 * @createTime 2021年11月09日 18:27:00
 */

//http://localhost:18086/search?keyword=%E6%89%8B%E6%9C%BA&brandId=1,2,3&categoryId=225&priceFrom=2000&priceTo=5000&store=true&prop=4:8G-12G&prop=5:128G-256G&sort=1&pageNum=2

@Service
public class SearchService {
    @Autowired
    private RestHighLevelClient restHighLevelClient;
    @Autowired
    private GmallPmsClient gmallPmsClient;
    @Autowired
    private GmallWmsClient gmallWmsClient;
    public SearchResponseVo search(SearchParamVo paramVo) {
        try {
            SearchResponse response = restHighLevelClient.search(new SearchRequest(new String[]{"goods"}, buildDsl(paramVo)), RequestOptions.DEFAULT);
            SearchResponseVo responseVo = this.parseResult(response);
            //分页参数，在搜索结果集中是无法获取的，只能从请求参数中获取
            responseVo.setPageNum(paramVo.getPageNum());
            responseVo.setPageSize(paramVo.getPageSize());
            return responseVo;
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }

    //构建搜索条件
    private SearchSourceBuilder buildDsl( SearchParamVo paramVo){
        //1.构建搜索和过滤条件
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        String keyword = paramVo.getKeyword();
        if(StringUtils.isEmpty(keyword)){
            throw new RuntimeException("没有匹配的商品");
        }
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        searchSourceBuilder.query(boolQueryBuilder);
        //1.1构建匹配查询

        boolQueryBuilder.must(QueryBuilders.matchQuery("title", keyword).operator(Operator.AND));
        //1.2构建过滤条件
            //1.2.1构建品牌过滤
        List<Long> brandId = paramVo.getBrandId();
        if(!CollectionUtils.isEmpty(brandId)){
            boolQueryBuilder.filter(QueryBuilders.termsQuery("brandId", brandId));
        }
        //1.2.2构建分类过滤
        List<Long> categoryId = paramVo.getCategoryId();
        if(!CollectionUtils.isEmpty(categoryId)){
            boolQueryBuilder.filter(QueryBuilders.termsQuery("categoryId", categoryId));
        }
        //1.2.3构建价格区间过滤
        Double priceFrom = paramVo.getPriceFrom();
        Double priceTo = paramVo.getPriceTo();
        //如果任何一个价格不为空，则需要添加价格区间查询
        if(priceFrom != null || priceTo != null){
            //构建一个价格区间查询，放入bool查询的过滤条件种
            RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("price");
            boolQueryBuilder.filter(rangeQuery);
            //如果起始价格不为空，则需要添加gte条件
            if(priceFrom != null){
                rangeQuery.gte(priceFrom);
            }
            //如果截至价格不为空，则需要添加lte条件
            if(priceTo != null){
                rangeQuery.lte(priceTo);
            }

        }
        //1.2.4构建是否有货
        Boolean store = paramVo.getStore();
        //为了方便后续测试，store为false也可以查询
        if(store != null){
            boolQueryBuilder.filter(QueryBuilders.termQuery("store", store));
        }
        //1.2.5构建规格参数过滤
        //获取搜索条件中规格参数的过滤条件列表
        List<String> props = paramVo.getProps();
        //遍历规格参数的过滤条件
        if(!CollectionUtils.isEmpty(props)){
            props.forEach(prop->{//3:8G-12G
//                先以冒号分割字符串
                String[] attrs = StringUtils.split(prop, ":");
                //如果分割出的字符串不为空，并且长度为2，并且以数字开头
                if(attrs != null && attrs.length==2 && NumberUtils.isCreatable(attrs[0])){
                    BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
                    boolQuery.must(QueryBuilders.termQuery("searchAttrs.attrId", attrs[0]));
                    String[] attrValues = StringUtils.split(attrs[1], "-");
                    boolQuery.must(QueryBuilders.termsQuery("searchAttrs.attrValue", attrValues));
                    boolQueryBuilder.filter(QueryBuilders.nestedQuery("searchAttrs",boolQuery, ScoreMode.None));

                }
            });
        }
        //2.构建排序条件
        Integer sort = paramVo.getSort();
        switch(sort){
            case 1:searchSourceBuilder.sort("price", SortOrder.DESC);break;
            case 2:searchSourceBuilder.sort("price", SortOrder.ASC);break;
            case 3:searchSourceBuilder.sort("sales", SortOrder.DESC);break;
            case 4:searchSourceBuilder.sort("createTime", SortOrder.DESC);break;
            default:
                searchSourceBuilder.sort("_score",SortOrder.DESC);
                break;

        }
        //3.构建分页
        Integer pageNum = paramVo.getPageNum();
        Integer pageSize = paramVo.getPageSize();
        searchSourceBuilder.from((pageNum-1)*pageSize);
        searchSourceBuilder.size(pageSize);


        //4.构建高亮
        searchSourceBuilder.highlighter(new HighlightBuilder().field("title").preTags("<font style='color:red;'>").postTags("</font>"));

//        5.构建聚合
//        5.1品牌聚合
        searchSourceBuilder.aggregation(AggregationBuilders.terms("brandIdAgg").field("brandId").
                subAggregation(AggregationBuilders.terms("brandNameAgg").field("brandName")).
                subAggregation(AggregationBuilders.terms("logoAgg").field("logo")));
        //5.2分类聚合
        searchSourceBuilder.aggregation(AggregationBuilders.terms("categoryIdAgg").field("categoryId").
                subAggregation(AggregationBuilders.terms("categoryNameAgg").field("categoryName")));
        //5.3规格参数聚合
//        searchSourceBuilder.aggregation(AggregationBuilders.nested("attrAgg", "searchAttrs").
//                subAggregation(AggregationBuilders.terms("attrIdAgg").field("searchAttrs.attrId").
//                        subAggregation(AggregationBuilders.terms("attrNameAgg").field("searchAttrs.attrName")).
//                        subAggregation(AggregationBuilders.terms("attrValueAgg").field("searchAttrs.attrValue"))));
        searchSourceBuilder.aggregation(AggregationBuilders.nested("attrAgg", "searchAttrs")
                .subAggregation(AggregationBuilders.terms("attrIdAgg").field("searchAttrs.attrId")
                        .subAggregation(AggregationBuilders.terms("attrNameAgg").field("searchAttrs.attrName"))
                        .subAggregation(AggregationBuilders.terms("attrValueAgg").field("searchAttrs.attrValue"))));
        //6构建结果集过滤
        searchSourceBuilder.fetchSource(new String[]{"skuId","title","subtitle","price","defaultImage"},null);
        System.out.println(searchSourceBuilder);
        return searchSourceBuilder;
    }


    private SearchResponseVo parseResult(SearchResponse response){
        SearchResponseVo responseVo = new SearchResponseVo();
        //解析搜索结果集 hits
        SearchHits hits = response.getHits();
        //总命中数
        responseVo.setTotal(hits.getTotalHits());
        //当前页的记录
        SearchHit[] hitsHits = hits.getHits();

        responseVo.setGoodsList(Arrays.stream(hitsHits).map(hitsHit->{
            //获取每一个记录中的_source --> Goods对象
            String json = hitsHit.getSourceAsString();
            //反序列化为goods对象
            Goods goods = JSON.parseObject(json, Goods.class);
            //替换普通的title,获取高亮的title
            Map<String, HighlightField> highlightFields = hitsHit.getHighlightFields();
            if(!CollectionUtils.isEmpty(highlightFields)){
                HighlightField highlightField = highlightFields.get("title");
                if(highlightField != null){
                    Text[] fragments = highlightField.getFragments();
                    if(fragments!=null&&fragments.length>0){
                        goods.setTitle(fragments[0].string());
                    }

                }
            }


            return goods;

        }).collect(Collectors.toList()));

        //解析聚合结果集 aggregation
        Aggregations aggregations = response.getAggregations();

        //获取品牌的聚合结果集
        ParsedLongTerms brandIdAgg = aggregations.get("brandIdAgg");
        List<? extends Terms.Bucket> brandIdAggBuckets = brandIdAgg.getBuckets();
        if(!CollectionUtils.isEmpty(brandIdAggBuckets)){
            //把桶集合转化成品牌集合
            responseVo.setBrands(brandIdAggBuckets.stream().map(bucket->{
                BrandEntity brandEntity = new BrandEntity();
                //桶的key就是品牌id
                brandEntity.setId(((Terms.Bucket) bucket).getKeyAsNumber().longValue());
                //获取子聚合
                Aggregations subAggs = (((Terms.Bucket) bucket).getAggregations());
                //品牌名称在名称子聚合中
                ParsedStringTerms brandNameAgg = subAggs.get("brandNameAgg");
                List<? extends Terms.Bucket> buckets = brandNameAgg.getBuckets();
                //如果品牌名称子聚合中的桶不为空，取第一个桶中的key
                if(!CollectionUtils.isEmpty(buckets)){
                    brandEntity.setName(buckets.get(0).getKeyAsString());
                }
                //获取品牌logo的子聚合
                ParsedStringTerms logoAgg = subAggs.get("logoAgg");
                List<? extends Terms.Bucket> logoAggBuckets = logoAgg.getBuckets();
                if(!CollectionUtils.isEmpty(logoAggBuckets)){
                    brandEntity.setLogo(logoAggBuckets.get(0).getKeyAsString());
                }
                return brandEntity;
            }).collect(Collectors.toList()));
        }

//        response.getAggregations();
        //获取分类的聚合结果集
        ParsedLongTerms categoryIdAgg = aggregations.get("categoryIdAgg");
        List<? extends Terms.Bucket> categoryBuckets = categoryIdAgg.getBuckets();
        if(!CollectionUtils.isEmpty(categoryBuckets)){
            responseVo.setCategories(categoryBuckets.stream().map(bucket->{
                CategoryEntity categoryEntity = new CategoryEntity();
                categoryEntity.setId(((Terms.Bucket) bucket).getKeyAsNumber().longValue());
                //获取分类名称子聚合
                ParsedStringTerms categoryNameAgg = ((Terms.Bucket) bucket).getAggregations().get("categoryNameAgg");
                List<? extends Terms.Bucket> buckets = categoryNameAgg.getBuckets();
                if(!CollectionUtils.isEmpty(buckets)){
                    categoryEntity.setName(buckets.get(0).getKeyAsString());
                }
                return categoryEntity;
            }).collect(Collectors.toList()));
        }
        //获取规格参数的聚合 嵌套聚合
        ParsedNested attrAgg = aggregations.get("attrAgg");
        //获取嵌套集合中的规格参数id集合
        ParsedLongTerms attrIdAgg = attrAgg.getAggregations().get("attrIdAgg");
        List<? extends Terms.Bucket> buckets = attrIdAgg.getBuckets();
        if(!CollectionUtils.isEmpty(buckets)){
            responseVo.setFilters(buckets.stream().map(bucket->{
                SearchResponseAttrValueVo attrValueVo = new SearchResponseAttrValueVo();
                attrValueVo.setAttrId(((Terms.Bucket) bucket).getKeyAsNumber().longValue());
                //桶中的子集合
                Aggregations subAggs = ((Terms.Bucket) bucket).getAggregations();
                //规格参数名称
                ParsedStringTerms attrNameAgg = subAggs.get("attrNameAgg");
                List<? extends Terms.Bucket> nameBuckets = attrNameAgg.getBuckets();
                if(!CollectionUtils.isEmpty(nameBuckets)){
                    attrValueVo.setAttrName(nameBuckets.get(0).getKeyAsString());
                }
                //规格参数值
                ParsedStringTerms attrValueAgg = subAggs.get("attrValueAgg");
                List<? extends Terms.Bucket> valueAggBuckets = attrValueAgg.getBuckets();
                if(!CollectionUtils.isEmpty(valueAggBuckets)){
                    attrValueVo.setAttrValues(valueAggBuckets.stream().map(Terms.Bucket::getKeyAsString).collect(Collectors.toList()));
                }
                return attrValueVo;
            }).collect(Collectors.toList()));
        }

        return responseVo;
    }
//    public void createIndex(Long id){
//        ResponseVo<List<SkuEntity>> skuResponseVo = this.gmallPmsClient.querySkuBySpuid(id);
//        List<SkuEntity> skuEntities = skuResponseVo.getData();
//        if(!CollectionUtils.isEmpty(skuEntities)){
//            //根据品牌的id查询品牌，同一个spu品牌是一样的，并且只有sku集合不为空时，，才需要品牌
//
//
//            //把sku集合转成goods集合
//            List<Goods> goodsList = skuEntities.stream().map(skuEntity -> {
//                Goods goods = new Goods();
//                //设置sku相关信息
//                goods.setSkuId(skuEntity.getId());
//                goods.setTitle(skuEntity.getTitle());
//                goods.setSubtitle(skuEntity.getSubtitle());
//                goods.setDefaultImage(skuEntity.getDefaultImage());
//                //设置排序分页及过滤
////                goods.setCreatTime(skuEntity.getCreateTime());
//                //库存查询
//                ResponseVo<List<WareSkuEntity>> wareSkuResponseVo = gmallWmsClient.queryWareSkuBySkuId(skuEntity.getId());
//                List<WareSkuEntity> wareSkuEntities = wareSkuResponseVo.getData();
//                if(!CollectionUtils.isEmpty(wareSkuEntities)){
//                    goods.setSales(wareSkuEntities.stream().map(WareSkuEntity::getSales).reduce((a,b) -> a+b).get());
//                    goods.setStore(wareSkuEntities.stream().anyMatch(wareSkuEntity -> wareSkuEntity.getStock() - wareSkuEntity.getStockLocked() >0 ));
//                }
//                ResponseVo<BrandEntity> brandEntityResponseVo = gmallPmsClient.queryBrandById(skuEntity.getBrandId());
//                BrandEntity brandEntity = brandEntityResponseVo.getData();
//                //根据分类id查询分类
//                ResponseVo<CategoryEntity> categoryEntityResponseVo = this.gmallPmsClient.queryCategoryById(skuEntity.getCategoryId());
//                CategoryEntity categoryEntity = categoryEntityResponseVo.getData();
//                //根据 分类ID和spuId查询基本类型的规格参数和值
//                ResponseVo<List<SpuAttrValueEntity>> spuAttrValueResponseVo = this.gmallPmsClient.querySearchAttrValueByCidAndSpuId(skuEntity.getCategoryId(), id);
//                List<SpuAttrValueEntity> spuAttrValueEntities = spuAttrValueResponseVo.getData();
//                //品牌
//                if (brandEntity != null) {
//                    goods.setBrandId(brandEntity.getId());
//                    goods.setBrandName(brandEntity.getName());
//                    goods.setLogo(brandEntity.getLogo());
//
//                }
//                //分类
//                if (categoryEntity == null) {
//                    goods.setCategoryId(categoryEntity.getId());
//                    goods.setCategoryName(categoryEntity.getName());
//                }
//                //规格参数
//                List<SearchAttrValueVo> searchAttrValueVos = new ArrayList<>();
//                //销售类型
//                ResponseVo<List<SkuAttrValueEntity>> skuAttrResponseVo = this.gmallPmsClient.querySearchAttrValueByCidAndSkuId(categoryEntity.getId(), skuEntity.getId());
//                List<SkuAttrValueEntity> skuAttrValueEntities = skuAttrResponseVo.getData();
//                if(!CollectionUtils.isEmpty(skuAttrValueEntities)){
//                    searchAttrValueVos.addAll(skuAttrValueEntities.stream().map(skuAttrValueEntity -> {
//                        SearchAttrValueVo searchAttrValueVo = new SearchAttrValueVo();
//                        BeanUtils.copyProperties(skuAttrValueEntity, searchAttrValueVo);
//                        return searchAttrValueVo;
//                    }).collect(Collectors.toList()));
//                }
//                //基本类型的规格参数
//                if(!CollectionUtils.isEmpty(spuAttrValueEntities)){
//                    searchAttrValueVos.addAll(spuAttrValueEntities.stream().map(spuAttrValueEntity -> {
//                        SearchAttrValueVo searchAttrValueVo = new SearchAttrValueVo();
//                        BeanUtils.copyProperties(spuAttrValueEntity, searchAttrValueVo);
//                        return searchAttrValueVo;
//                    }).collect(Collectors.toList()));
//                }
//                goods.setSearchAttrs(searchAttrValueVos);
//                    }
//                    Index action = new Index.Builder(goodsVO).index("goods").type("info").id(skuInfoEntity.getSkuId().toString()).build();
//            try {
//                jestClient.execute(action);
//            } catch (IOException e) {
//                e.printStackTrace();
//            }
//        });
//    }
}
